#include "mainwindow.h"
//#include <windows.h>
#include <QXmlStreamWriter>
#include <QMessageBox>
#include <QTextStream>
#include <QTimer>
#include "ui_mainwindow.h"
#include "aboutdialog.h"

#include <stdexcept>

#define CONFIGFILE          "config.xml"
#define TWITTER_MAX_CHARS   "140"

/*
 *  qt-win-opensource-4.8.4-mingw
 *  qtcreator-2.7.1
 *  MinGW-gcc-4.4.0-2
 */

QString MainWindow::s_qsBaudRates = "110,300,600,1200,2400,4800,9600,14400,19200,38400,57600,115200,128000,2560008";

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    ui->baudRateComboBox->addItems( s_qsBaudRates.split( "," ) );

    QTimer* serialIncomingDataMonitor = new QTimer(this);
    connect( serialIncomingDataMonitor, SIGNAL( timeout( ) ), this, SLOT( checkForIncomingData( ) ) );
    serialIncomingDataMonitor->start( 1000 );
    connect( ui->actionSaveConfig,  SIGNAL( triggered( ) ),                 this, SLOT( saveButtonClicked( ) ) );
    connect( ui->tweetButton,       SIGNAL( clicked( ) ),                   this, SLOT( tweetButtonClicked( ) ) );
    connect( ui->tweetTextEdit,     SIGNAL( textChanged( ) ),               this, SLOT( tweetTextChanged( ) ) );
    connect( ui->portLineEdit,      SIGNAL( textChanged( QString ) ),       this, SLOT( portTextChanged( QString ) ) );
    connect( ui->actionConnect,     SIGNAL( triggered( ) ),                 this, SLOT( serialConnect( ) ) );
    connect( ui->baudRateComboBox,  SIGNAL( currentIndexChanged ( int ) ),  this, SLOT( baudRateChanged( int ) ) );
    connect( ui->actionAbout,       SIGNAL( triggered( ) ),                 this, SLOT( aboutButtonClicked( ) ) );

    m_pLogger = new Logger( ui->logTextEdit );
    ui->remainingLabel->setText( QString( TWITTER_MAX_CHARS ) );
    ui->saveRemindlabel->setVisible( false );
    m_bHasSettingsChanged = false;


    QFile file( CONFIGFILE );

    if( file.exists( ) && file.open( QIODevice::ReadOnly ) ) {
        QDomDocument doc;
        doc.setContent( &file );
        populateSettings( &doc );
        file.close( );
    } else {
        ui->tabWidget->setCurrentIndex( 1 );
        QMessageBox::warning( this, tr( "No Configuration File" ), tr( "Please enter your Arduino settings and click File->Save." ));
    }

    file.close( );
}

MainWindow::~MainWindow() {
    delete m_pLogger;
    delete m_pSerial;
    delete ui;
}

void MainWindow::saveButtonClicked( ) {
    QFile file( CONFIGFILE );

    if( file.exists( ) && file.open( QIODevice::ReadOnly ) ) {
        QDomDocument doc;
        if( doc.setContent( &file ) ) {
            file.close();

            QDomElement root = doc.documentElement( );
            if( !root.isNull( ) ) {
                QDomElement settings = root.elementsByTagName( "settings" ).at( 0 ).toElement( );

                if( !settings.isNull( ) )
                    root.removeChild( settings );   //prevents repeat elements being written.

                settings = doc.createElement( "settings" );
                root.insertAfter( settings, QDomNode( ) ); {
                     QDomElement portElement = doc.createElement( "port" ); {
                         settings.insertAfter( portElement, QDomNode( ) );
                         QDomText portText = doc.createTextNode( ui->portLineEdit->text( ) );
                         portElement.appendChild( portText );
                     }

                     QDomElement baudRateElement = doc.createElement( "baudRate" ); {
                         baudRateElement.setAttribute( "index", ui->baudRateComboBox->currentIndex( ) );
                         settings.insertAfter( baudRateElement, QDomNode( ) );
                         QDomText baudRatesText = doc.createTextNode( ui->baudRateComboBox->currentText( ) );
                         baudRateElement.appendChild( baudRatesText );
                     }
                }
            }

            //Save new XML
            QTextStream stream( &file );
            if( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) {
                QMessageBox::critical( this, tr( "Write Error" ), tr( "Unable to write configuration file." ) );
            } else {
                doc.save(stream, 4);
                QMessageBox::information(this, tr( "Save Complete" ), tr( "Settings have been saved." ) );
                ui->tabWidget->setCurrentIndex( 0 );
                populateSettings( &doc );
            }
            m_bHasSettingsChanged = false;
            file.close();
        }
    } else {
        file.open( QIODevice::WriteOnly );
        QXmlStreamWriter xmlWriter( &file );
        xmlWriter.setAutoFormatting( true );
        xmlWriter.writeStartDocument( );
        xmlWriter.writeStartElement( "config" );
        xmlWriter.writeEndElement( );
        xmlWriter.writeEndDocument( );
        file.close( );
        saveButtonClicked( );
    }
}

void MainWindow::tweetButtonClicked() {
    if( m_pSerial->IsConnected( ) ) {
        QString sMsg = ui->tweetTextEdit->toPlainText( );
        sMsg.append( 0x03 );    //append message with a 'EOT'
        QByteArray msgByteArray = sMsg.toUtf8( );

        if( m_pSerial->WriteData( msgByteArray.constData( ), sMsg.length( ) ) ) {
            m_pLogger->log( "Your tweet has been sent to your Arduino" );
            ui->tweetTextEdit->clear( );
        } else {
            QString strMsg = "Unfortunately, there was an error with your tweet...";
            QMessageBox::warning( NULL, "Dead Bird", strMsg );
            m_pLogger->log( strMsg );
        }
    } else {
        m_pLogger->log("Serial Connection Lost");
        ui->tweetButton->setEnabled( false );
        ui->tweetTextEdit->setEnabled( false );
    }
}

void MainWindow::tweetTextChanged( ) {
    int nTweetLength = ui->tweetTextEdit->toPlainText( ).length( );
    int nRemainingChars = QString( TWITTER_MAX_CHARS ).toInt( ) - nTweetLength;

    if( nTweetLength > 0 )
        ui->tweetButton->setEnabled( true );
    else
        ui->tweetButton->setEnabled( false );

    if( nRemainingChars < 0 ) {
        ui->tweetTextEdit->textCursor( ).deletePreviousChar( );
        nRemainingChars = 0;
    }
    ui->remainingLabel->setText( QString::number( nRemainingChars ) );
}

void MainWindow::aboutButtonClicked() {
    AboutDialog* pAboutDialog = new AboutDialog(this);
    pAboutDialog->exec();
}

void MainWindow::portTextChanged( QString strText ) {
    Q_UNUSED( strText );
    if( ui->tabWidget->currentIndex() == 1 )
        m_bHasSettingsChanged = true;
}

void MainWindow::baudRateChanged( int iIndex ) {
    Q_UNUSED( iIndex );
    if( ui->tabWidget->currentIndex() == 1 )
        m_bHasSettingsChanged = true;
}

void MainWindow::serialConnect() {
    QString strPort = QString( "\\\\.\\%1").arg( ui->portLineEdit->text( ) );
    QByteArray portByteArray = strPort.toUtf8( );
    const char* zpPort = portByteArray.constData();

    m_pLogger->log( QString("Connected on port: %1").arg( ui->portLineEdit->text( ) ) );
    m_pSerial = new Serial( zpPort, ui->baudRateComboBox->currentText( ).toInt( ), m_pLogger );

    if ( m_pSerial->IsConnected( ) )
        ui->tweetTextEdit->setEnabled( true );
}

void MainWindow::checkForIncomingData() {
    if( m_bHasSettingsChanged )
        ui->saveRemindlabel->setVisible( true );
    else
        ui->saveRemindlabel->setVisible( false );

    if( m_pSerial &&  m_pSerial->IsConnected( ) ) {
        ui->actionConnect->setEnabled( false );
        char buffer[256];
        memset( &buffer[ 0 ], 0, sizeof( buffer ) );
        m_pSerial->ReadData( buffer, sizeof( buffer ) );

        QString strResponse = QString( buffer ).toAscii( );

        if( strResponse.length() > 0 ) {
            m_pLogger->log( QString( "Serial Data Length: %1").arg(strResponse.length( ) ) );
            ui->responseTextEdit->appendPlainText( strResponse );
        }
    } else {
        ui->actionConnect->setEnabled( true );
    }

}

void MainWindow::closeEvent(QCloseEvent *pEvent) {
    Q_UNUSED( pEvent );
}

void MainWindow::populateSettings( QDomDocument *pDoc ) {
    m_pLogger->log( "Reading Configurations..." );
    QDomElement root = pDoc->documentElement( );

    try {
        if( !root.isNull( ) ) {
            QDomElement settings = root.elementsByTagName( "settings" ).at( 0 ).toElement( );
            if( !settings.isNull( ) ) {
                QDomElement port = settings.elementsByTagName( "port" ).at( 0 ).toElement( );
                if ( !port.isNull( ) )
                    ui->portLineEdit->setText( port.text( ) );
                else
                    throw std::runtime_error("Error locating port element.");

                QDomElement baudRate = settings.elementsByTagName( "baudRate" ).at( 0 ).toElement( );
                if ( !baudRate.isNull( ) )
                    ui->baudRateComboBox->setCurrentIndex( baudRate.attribute( "index" ).toInt( ) );
                else
                    throw std::runtime_error("Error locating apiKey element.");

            } else {
                throw std::runtime_error("Error locating settings element.");
            }
        } else {
            throw std::runtime_error("Error locating root element.");
        }
        m_pLogger->log( "Configuration Read Successfully, Please Click Tools->Connect." );
    } catch( const std::runtime_error &e ) {
        QMessageBox::critical( this, tr( "Configuration File Read Error" ), e.what( ) );
    }
}

